package com.bluexmicro.ota.customface

import android.graphics.Bitmap
import android.graphics.Color
import android.util.Log
import com.bluexmicro.bluetooth.toHexString
import com.bluexmicro.bluetooth.toUInt8
import com.bluexmicro.ota.asset.AssetBean
import com.bluexmicro.ota.asset.AssetType
import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.util.zip.CRC32

data class TextOffset(
    val left: Int,
    val top: Int
)


/**
 * @param screenWidth 屏幕宽度
 * @param screenHeight 屏幕宽度
 * @param backgroundStartIndex 模版文件中储存背景数据的开始下标
 * @param scriptStartIndex 模版文件中储存脚本开始下标
 * @param scriptLen 模版文件中储存脚本的长度
 * @param templateRawData 模版文件的原始数据
 */
data class CustomFaceModel(
    val screenWidth: Int,
    val screenHeight: Int,
    val backgroundStartIndex: Int,
    val scriptStartIndex: Int,
    val scriptLen: Int,
    val templateRawData: ByteArray
) {

    /**
     * @param bitmap 裁剪后的bitmap对象
     * @param textOffset 自定义表盘上文字的位移,
     */
    fun createAsset(bitmap: Bitmap, textOffset: TextOffset? = null): AssetBean {

        if (bitmap.width != screenWidth && bitmap.height != screenHeight) {
            throw IllegalArgumentException("Bitmap尺寸不对，Bitmap的宽高必须和[screenWidth,screenHeight]一致，请对图片进行裁剪再试！")
        }

        //
        val newBackgroundData = createBackgroundData(bitmap)
        Log.e("TAG", "newBackgroundData.size: ${newBackgroundData.size}")
        var rawData = templateRawData.copyOf()
        System.arraycopy(
            newBackgroundData,
            0,
            rawData,
            backgroundStartIndex,
            newBackgroundData.size
        )

        if (textOffset != null) {
            //修改文字位置
            rawData = setCoordinates(rawData, textOffset)
        } else {
            rawData = setCoordinates(rawData, TextOffset(0, 100))
        }

        val pendingCheckSum = rawData.copyOfRange(12, rawData.size)
        val bytes = pendingCheckSum.run {
            val crc32 = CRC32()
            crc32.update(this)
            val crcValue = crc32.value.toInt()
            val bb = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN)
            bb.putInt(crcValue)
            bb.array()
        }
        System.arraycopy(bytes, 0, rawData, 4, 4)

        val crc32 = CRC32()
        crc32.update(rawData)
        val crc32Value = crc32.value.toInt()

        return AssetBean(
            type = AssetType.FACE,
            name = "Custom Face",
            rawData = rawData,
            crc32 = crc32Value
        )
    }

    private fun getUInt16(byteArray: ByteArray, ofs: Int): Int {
        return (byteArray[ofs].toUInt8().shl(8)).or(byteArray[ofs + 1].toUInt8())
    }

    private fun setUInt16(byteArray: ByteArray, ofs: Int, v: Int) {
        byteArray[ofs] = v.shr(8).toByte()
        byteArray[ofs + 1] = v.and(0xff).toByte()
    }

    private fun setCoordinates(data: ByteArray, textOffset: TextOffset): ByteArray {

        val cmdLen = listOf(
            0+1,
            3+1,
            7+1,
            6+1,
            6+1,
            10+1,
            14+1,
            11+1,
            12+1,
            14+1,
            7+1,
            2+1,
            3+1,
            4+1,
            6+1,
            18+1,
            8+1,
            14+1,
            16+1,
            8+1,
        )

        val scriptData = ByteArray(scriptLen)
        System.arraycopy(data, scriptStartIndex, scriptData, 0, scriptLen)

        Log.e("TAG", "scriptData: ${scriptData.toHexString(' ')}")

        var ofs = 0
        var stop = false
        do {
            val cmd = scriptData[ofs].toUInt8()
            Log.e("TAG", "cmd: $cmd")
            when (cmd) {
                0 -> {
                    //脚本结束
                    ofs += cmdLen[0]
                    stop = true
                }

                1 -> {
                    ofs += cmdLen[1]
                }

                2 -> {
                    ofs += cmdLen[2];
                }

                3 -> {
                    ofs += cmdLen[3] // 同上写法
                }

                4 -> {
                    ofs += cmdLen[4]
                }

                5 -> {
                    // +1是指令名本身占了一个byte
                    val pic_id: Int = getUInt16(scriptData, ofs + 1)
                    var x: Int = getUInt16(scriptData, ofs + 2 + 1)
                    var y: Int = getUInt16(scriptData, ofs + 4 + 1)
                    Log.e("TAG", "x,y = ($x,$y)" )
                    // 开始坐标运算
                    x += textOffset.left
                    y += textOffset.top

                    // 把运算结果保存会原数据里
                    setUInt16(scriptData, ofs + 2 + 1, x)
                    setUInt16(scriptData, ofs + 4 + 1, y)

                    // 再读一次,检查有没写入正确
                    x = getUInt16(scriptData, ofs + 2 + 1)
                    y = getUInt16(scriptData, ofs + 4 + 1)
                    Log.e("回读", "x,y = ($x,$y)" )

                    ofs += cmdLen[5] // 同上写法
                }

                6 -> {
                    ofs += cmdLen[6]
                }

                7 -> {
                    ofs += cmdLen[7]
                }
                8 -> {
                    ofs += cmdLen[8]
                }
                9 -> {
                    ofs += cmdLen[9]
                }
                10 -> {
                    ofs += cmdLen[10]
                }

                11-> {
                    ofs += cmdLen[11]
                }
                12 -> {
                    ofs += cmdLen[12]
                }
                13-> {
                    ofs += cmdLen[13]
                }
                14 -> {
                    ofs += cmdLen[14]
                }
                15 -> {
                    ofs += cmdLen[15]
                }
                16 -> {
                    ofs += cmdLen[16]
                }
                17-> {
                    ofs += cmdLen[17]
                }
                18 -> {
                    ofs += cmdLen[18]
                }
                19-> {
                    ofs += cmdLen[19]
                }

                else -> {
                    // 发生异常错误
                    stop = true
                }
            }
        } while (!stop && ofs < scriptLen)

        val newData = data.clone()
        System.arraycopy(scriptData, 0, newData, scriptStartIndex, scriptLen)
        return newData
    }

    /**
     * 转化成手表能识别的图片数据
     */
    private fun createBackgroundData(bitmap: Bitmap): ByteArray {
        val bytes = mutableListOf<Byte>()
        for (y in 0 until bitmap.height) {
            for (x in 0 until bitmap.width) {
                val colorInt = bitmap.getPixel(x, y)
                val red = Color.red(colorInt)
                val green = Color.green(colorInt)
                val blue = Color.blue(colorInt)

                val b = blue.shr(3).and(0x1f)
                val g = green.shr(2).and(0x3f).shl(5)
                val r = red.shr(3).and(0x1f).shl(11)
                val rgb = r.or(g).or(b)

                val low = rgb.and(0xff).toByte()
                val high = rgb.and(0xff00).shr(8).toByte()

                bytes.add(low)
                bytes.add(high)
            }
        }
        return bytes.toByteArray()
    }

    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (javaClass != other?.javaClass) return false

        other as CustomFaceModel

        if (screenWidth != other.screenWidth) return false
        if (screenHeight != other.screenHeight) return false
        if (backgroundStartIndex != other.backgroundStartIndex) return false
        if (scriptStartIndex != other.scriptStartIndex) return false
        if (scriptLen != other.scriptLen) return false
        if (!templateRawData.contentEquals(other.templateRawData)) return false

        return true
    }

    override fun hashCode(): Int {
        var result = screenWidth
        result = 31 * result + screenHeight
        result = 31 * result + backgroundStartIndex
        result = 31 * result + scriptStartIndex
        result = 31 * result + scriptLen
        result = 31 * result + templateRawData.contentHashCode()
        return result
    }

}